/* Includes ------------------------------------------------------------------*/
#include "oled.h"
#include "gpio.h"
#include "oledfont.h"

/*----------------------------------------------------------------------------*/
/* Configure oled                                                             */
/*----------------------------------------------------------------------------*/

void IIC_Start(void) {
  OLED_SCL_Set();
  OLED_SDA_Set();
  OLED_SDA_Clr();
}

void IIC_Stop(void) {
  OLED_SCL_Set();
  OLED_SDA_Clr();
  OLED_SDA_Set();
}

void IIC_Wait_Ack(void) {
  OLED_SCL_Set();
  OLED_SCL_Clr();
}

void Write_IIC_Byte(uint8_t IIC_Byte) {
  uint8_t i;

  OLED_SCL_Clr();

  for (i = 0; i < 8; i++) {
    if (IIC_Byte & 0x80) {
      OLED_SDA_Set();
    } else {
      OLED_SDA_Clr();
    }
    IIC_Byte <<= 1;
    OLED_SCL_Set();
    OLED_SCL_Clr();
  }
}

void Write_IIC_Command(uint8_t IIC_Command) {
  IIC_Start();
  Write_IIC_Byte(0x78);
  IIC_Wait_Ack();
  Write_IIC_Byte(0x00);
  IIC_Wait_Ack();
  Write_IIC_Byte(IIC_Command);
  IIC_Wait_Ack();
  IIC_Stop();
}

void Write_IIC_Data(uint8_t IIC_Data) {
  IIC_Start();
  Write_IIC_Byte(0x78);
  IIC_Wait_Ack();
  Write_IIC_Byte(0x40);
  IIC_Wait_Ack();
  Write_IIC_Byte(IIC_Data);
  IIC_Wait_Ack();
  IIC_Stop();
}

void OLED_WR_Byte(uint8_t dat, uint8_t cmd) {
  if (cmd) {
    Write_IIC_Data(dat);
  } else {
    Write_IIC_Command(dat);
  }
}

void OLED_Clear(void) {
  uint8_t i, n;
  for (i = 0; i < 8; i++) {
    OLED_WR_Byte (0xb0 + i, OLED_CMD);
    OLED_WR_Byte (0x00, OLED_CMD);
    OLED_WR_Byte (0x10, OLED_CMD);
    for (n = 0; n < 128; n++) {
      OLED_WR_Byte(0, OLED_DATA);
    }
  }
}

void OLED_Set_Pos(unsigned char x, unsigned char y) {
  OLED_WR_Byte(0xb0 + y, OLED_CMD);
  OLED_WR_Byte(((x & 0xf0) >> 4) | 0x10, OLED_CMD);
  OLED_WR_Byte((x & 0x0f), OLED_CMD);
}

void OLED_ShowChar(uint8_t x, uint8_t y, uint8_t chr) {
  uint8_t i = 0;

  if (x > 20) return;

  x *= 6;

  OLED_Set_Pos(x, y);
  for (i = 0; i < 6; i++) {
    OLED_WR_Byte(F6x8[chr - ' '][i], OLED_DATA);
  }
}

void OLED_ShowString(uint8_t x, uint8_t y, uint8_t *chr) {
  uint8_t j = 0;

  while (chr[j] != '\0') {
    OLED_ShowChar(x, y, chr[j]);
    x += 1;
    j++;
  }
}

void OLED_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct;

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(oled_GPIO_Port, oled_scl_Pin | oled_sda_Pin, GPIO_PIN_SET);

  /*Configure GPIO pins : PBPin PBPin */
  GPIO_InitStruct.Pin = oled_scl_Pin | oled_sda_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(oled_GPIO_Port, &GPIO_InitStruct);

  //Set Display Off
  OLED_WR_Byte(0xae, OLED_CMD);

  //Set lower column address
  OLED_WR_Byte(0x00, OLED_CMD);
  OLED_WR_Byte(0x10, OLED_CMD);

  //Set display start line
  OLED_WR_Byte(0x00, OLED_CMD);

  //Set page address
  OLED_WR_Byte(0xb0, OLED_CMD);

  //Set Contrast Control
  OLED_WR_Byte(0x81, OLED_CMD);
  OLED_WR_Byte(0xff, OLED_CMD);

  //Set Segment Re-Map
  OLED_WR_Byte(0xa1, OLED_CMD);

  //normal / reverse
  OLED_WR_Byte(0xa6, OLED_CMD);

  //Set Multiplex Ratio
  OLED_WR_Byte(0xa8, OLED_CMD);
  OLED_WR_Byte(0x1f, OLED_CMD);

  //Set COM Output Scan Direction
  OLED_WR_Byte(0xC8, OLED_CMD);

  //Set Display Offset
  OLED_WR_Byte(0xd3, OLED_CMD);
  OLED_WR_Byte(0x00, OLED_CMD);

  //Set Display Clock Divide Ratio/Oscillator Frequency
  OLED_WR_Byte(0xd5, OLED_CMD);
  OLED_WR_Byte(0x80, OLED_CMD);

  //Set Pre-Charge Period
  OLED_WR_Byte(0xd9, OLED_CMD);
  OLED_WR_Byte(0x1f, OLED_CMD);

  //Set COM Pins Hardware Configuration
  OLED_WR_Byte(0xda, OLED_CMD);
  OLED_WR_Byte(0x00, OLED_CMD);

  //Set VCOMH Deselect Level
  OLED_WR_Byte(0xdb, OLED_CMD);
  OLED_WR_Byte(0x40, OLED_CMD);

  //Set charge pump enable
  OLED_WR_Byte(0x8d, OLED_CMD);
  OLED_WR_Byte(0x14, OLED_CMD);

  //Set Display On
  OLED_WR_Byte(0xaf, OLED_CMD);

  OLED_Clear();
}
